package net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

import net.cathackers.devel.exceptions.CATException;
import net.cathackers.devel.scmp.auth.interfaces.UserID;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.interfaces.IQHandler;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.interfaces.IQHandlerClassifier;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.interfaces.Observable;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.interfaces.XMPPComponent;
import net.cathackers.devel.scmp.chat.impl.xmpp.presence.PresenceComponent;
import net.cathackers.devel.scmp.chat.impl.xmpp.presence.PresenceHandler;
import net.cathackers.devel.scmp.chat.interfaces.ChatManager;
import net.cathackers.devel.scmp.factories.SCMPFactory;
import net.cathackers.devel.scmp.xmpp.IQPacket;
import net.cathackers.devel.scmp.xmpp.Packet;
import net.cathackers.devel.scmp.xmpp.PresencePacket;
import net.cathackers.devel.scmp.xmpp.StanzaErrors;

public class IQComponent extends XMPPComponent<Packet> implements
	Observable<IQHandler> {
    // ConcurrentHashMap isn't required because no problem could arise because
    // of synchro. except in registeration
    // which will be done automatically by the server.
    private Map<IQHandlerClassifier, IQHandler> handlers_;
    private ChatManager manager_;
    private Logger logger_;
    private PresenceComponent presenceComponent_;
    private static Hashtable<String, Lock> lockingTable = new Hashtable<String, Lock>();

    public IQComponent() {
	logger_ = SCMPFactory.getLogger(this);
	manager_ = SCMPFactory.getChatManager();
	handlers_ = Collections
		.synchronizedMap(new HashMap<IQHandlerClassifier, IQHandler>());
	presenceComponent_ = new PresenceComponent(new PresenceHandler(
		SCMPFactory.getRosterSubscriptionManager()));

	// Loading the IQHanlders
	new IQHandlersLoader().LoadIQHandlers(this);
    }

    public void process(Packet packet) {
	/***
	 * This method accepts two types of packets * PresencePacket * IQ
	 * Packets
	 */
	Lock myLock = null;
	String from = packet.getBaseStreamID();
	try {
	    if (from != null) {
		// first check if it's already
		if (lockingTable.containsKey(from)) {
		    myLock = lockingTable.get(from);
		} else {
		    myLock = new ReentrantLock();
		    lockingTable.put(from, myLock);
		}
		try {
		    myLock.lockInterruptibly();
		    logger_
			    .debug("This thread has been locked, no IQ/Presence operations are allowed on "
				    + from);
		} catch (InterruptedException e) {
		    e.printStackTrace();
		    logger_
			    .error("Locking was interrupted, dropping this packet");
		    return;
		}
	    }
	    if (packet instanceof PresencePacket) {

		PresencePacket presence = (PresencePacket) packet;
//		logger_.debug("the IQComponent received a presence packet from user "+ from);
		presenceComponent_.process(presence);
		logger_.debug("DONE with this PRESENCE Packet");
	    } else {
		IQPacket iqPacket = (IQPacket) packet;
		String name = iqPacket.getName();
		String namespace = iqPacket.getNamespace();
		logger_.debug("Received IQ packet with name '" + name
			+ "' and namespace '" + namespace);
		IQHandlerClassifier classifier = new IQHandlerClassifierImpl(
			name, namespace);
		if (handlers_.containsKey(classifier)) {
		    handlers_.get(classifier).process(iqPacket);
		    logger_.debug("DONE with this IQ Packet");
		} else {
		    logger_
			    .warn("IQ Message with no registered handler was received)");
		    logger_.trace(packet.toXML());
		    packet.setError(new StanzaErrors(
			    StanzaErrors.Condition.feature_not_implemented));
		    packet.switchDirection();
		    manager_.deliverToUser(packet);
		    // TODO: create an non-implemented feature packet and send
		    // to
		    // deliver
		    // TODO: manager_.deliverToUser(packet);

		}
	    }
	} catch (CATException e) {
	    logger_.error("Caught Exception", e);
	} finally {
	    if (myLock != null) {
		logger_
			.debug("This thread has been unlocked, operations are allowed on "
				+ from);
		myLock.unlock();
		// if (((ReentrantLock)myLock).getHoldCount() == 0) {
		// lockingTable.remove(from.getID());
		// }
	    }
	}
	    logger_.debug("OUT OF HERE, READY FOR NEXT PACKET");
    }

    public boolean register(IQHandler listener) {
	boolean ret;
	if (handlers_.containsKey(listener.getClassifier())) {
	    ret = false;
	} else {
	    handlers_.put(listener.getClassifier(), listener);
	    logger_.debug("IQ Handler " + listener.getClass().getSimpleName()
		    + " was registed to hook to NS "
		    + listener.getClassifier().toString());
	    ret = true;
	}
	return ret;
    }

    public boolean unregister(IQHandler listener) {
	boolean ret;

	if (handlers_.containsKey(listener.getClassifier())) {
	    handlers_.remove(listener.getClassifier());
	    ret = true;
	} else {
	    ret = false;
	}
	return ret;
    }

}